home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Software Vault: The Gold Collection
/
Software Vault - The Gold Collection (American Databankers) (1993).ISO
/
cdr49
/
130_01.zip
/
RY.USE
< prev
next >
Wrap
Text File
|
1993-06-01
|
10KB
|
213 lines
Ry.use, a tutorial on the use of the random file code.
Ry.c consists of a set of functions that allow direct random access of
a disk file. This access is on a byte addressable level, using 'long integers'
as provided in BDS C by the long.c functions. The base functions are 'rgetc()'
and 'rputc()'. All other character handling functions (i.e. 'rprintf()') are
built upon these two. For details of calling conventions, etc. refer to the
file 'ry.doc'. One inportant detail, this code requires a z-80 processor to
run.
Internally, a file created by the ry code ('rcreat()') is identical to
any other cp/m text file with one exception. The first sector (known as sector
'0' to cp/m) is reserved as a file header for use by 'rcreat()', 'ropen()',
etc. As such, the 'first' byte of a file comes from and goes to the first byte
of sector 1. This offset is maintained throught the file. This is only
important, however, if you use other tools (such as ddt) to diddle with a
random file. Requests to seek any byte automatically add this offset to the
argument. Using the cpm command 'type' will also cause some interesting
results since the header (sector 0) will typically contain integer values that
will send the console into a fit. And if a 0x1a (^z) exists anywhere in the
header you will never even see the file proper. This inconvenience is
justified in most cases by the added power that this header allows when
handeling files in a random fashion.
The first half of this header, i.e. 64 bytes, is reserved for use by
'rcreat()', 'ropen()', and 'rclose()'. The first 4 bytes are for a long
integer pointer to the EOF. The ry code presently DOES NOT AUTOMATICALLY place
this value for you. It is the programmers responsibility to do so, if this
information is needed for the next time that the file is used. Future versions
will take care of this also. Bytes 5-8 are reserved for a timestamp of the
date of file creation, bytes 9-12 for a timestamp of the date of last access.
Again, these fields are not yet filled by the present version of ry code. The
remaining bytes of the first half are as yet undefined but reserved.
The second half of the header is for use by the programmer in any way
that he/she wishes. Typically it might contain links to other files, or
perhaps pointers used to separate different logical parts of a file.
When a file is opened via 'ropen()' or 'rcreat()' the function call
returns a pointer to a structure of type _file. This pointer is used by all
random function calls to refer to the file. In 'ry.h' a #define equates the
string "RFILE" to "struct _file", thus you declare a variable to hold the
returned value of 'ropen()' or 'rcreat()' as RFILE *<variable_name>. What you
are doing is declaring the variable to be a pointer to an 'RFILE' or 'struct of
type _file'.
As an example, let's create a file to hold to hold a list of names.
First we declare the variable used to hold the file pointer as:
RFILE *foo;
Next we creat the file:
foo = rcreat("a:namefile", 'd', 8);
The first arg is obvious, the name of the file we wish to create. The
second arg is a char that defines the mode in which we want the file to be
created/opened. In this case we used a 'd' to ask that the file be opened for
'direct' (read and write) access. Other modes include 'r' for read only and
'w' for write only. The unix file mode 'a' (append) is not implimented yet.
The third arg is the number of sectors that you want the system to use for
buffering the file. A large number here speeds things up by cutting down on
the number of disk accesses, but at the cost of using a large amount of memory.
Remember that this number refers to logical cp/m sectors, size of 128 bytes.
In this example I used 8 so that exactly 1 physical sector (my bios uses 1k
sectors) would be buffered, thus optimizing disk reads.
The syntax is identical for 'ropen()'. A file may be opened with a
different declared access mode and buffersize than that used when it was
created. The file pointer (foo in this case) is used as an arg for all other
references to this file. It is the sole arg to 'rclose()'. Be sure to check
the file pointer for a value of ERROR (-1) when opening or creating a file. If
an ERROR is returned DO NOT 'rclose()' the file, this will bash the system!
This is because of the fact that 'rclose()' returns bufferspace to the dynamic
memory pool maintained by 'alloc()' and 'free()'. This brings up one more
important point, BE SURE TO HAVE THE STATEMENT '_allocp = NULL;' as the first
item of any program using ry functions. This also means that alloc() and
free() must be enabled in your std libraries.
Next, let's add a couple names to the file:
rputs("freddy fudd", foo);
rputs("sally sue", foo);
Here the first arg is the string that we want to put in the file, and
the second is the file pointer. Ry.doc gives details for the use of other
calls such as 'rputl()' and 'rprintf()'. All of the commands for getting
things back out are also documented there.
To retrieve the names we first have to get back to the begginning of
the file:
lseek(foo, "0", 3);
The first arg is obviously the file pointer. The second is the byte we
want, expressed as an ascii string. The third arg tells 'lseek()' that the
second arg is a pointer to an ascii representation of the sought after byte.
The possible values for the third arg are:
a: 0, (used above) meaning that the second arg is the address of a 4
byte array holding a 'long int', the value of which is the address of the byte
we want to access. This address is relative to the first byte of the file.
b: 1, as in 0 above, the second arg is an array holding a 'long int',
but this time the address it represents is relative to the present location in
the file.
c: 2 is reserved for 'relative to end of file', not implimented yet.
d: 3 means that the second arg is the address of an array holding an
ascii representation of the byte address we want to access. 'lseek()' converts
this string into the appropriate value before seeking the byte address.
e: 4, same as 3, but address is relative to present location in file
(as in 1).
f: 5, reserved for 'ascii relative from end of file', not implimented
yet.
Now that we have sought byte 0 from beginning of file we can reread it.
char firstname[30], secondname[30]; /* declare strings to hold names */
rgets(firstname, foo);
rgets(secondname, foo);
The first arg is the destination of the string, the second the file
pointer.
Next we might want to mark the EOF:
rputc(0x1a, foo);
This just placed a control z in the file, following the NULL byte that
terminated the string "sally sue".
Next we want to back up 1 byte so that we are at the '^z':
lseek(foo, "-1", 3)
We asked to go back 1 byte from the present file location. We are now
in the file at the position of the '^z' byte following the string "sally sue".
We could record this position in the file header by:
ltell(foo, foo + 1);
The first arg is the file pointer, the second is the address of a 4
byte array that we want a 'long int' to be placed. This 'long int' is the
address of the current position in the file. When a file is opened the header
kept in sector 0 is placed in the buffer, between the struct that describes
the file and the buffered file data. The first four bytes of this header are
reserved for the EOF address, thus we ask 'ltell()' to place the current file
position (1 byte beyond the ^z) into the first 4 bytes following the struct
pointed at by foo. Remember that foo is a pointer to a struct of type _file,
thus 'foo + 1' evaluates to the address following the struct, not 1 byte beyond
the beginning of foo! Note that there is no way to make 'ltell()' return an
ascii form of the address. You would have to use 'ltoa()' to make this
conversion if needed.
Now to close the file:
rclose(foo);
This causes the buffer to be flushed to the disk if necessary and the
header to be updated to disk. Thus the address of the EOF has been recorded,
we need not read the whole file next time to find the end. After opening it we
merely:
lseek(foo, foo + 1, 0);
Here we told 'lseek()' to seek the address held in the first 4 bytes of
the header, that address being a 'long int', relative to beginning of the file.
Here's a complete program to help illustrate some of these ideas. It
is used to creat an empty mailbox used by a networking program. It first tries
to open the file, if successful it terminates the program, not wanting to
destroy the current box. Otherwise it creates the file and puts a ^z in the
first byte to mark the EOF. It then records the EOF address in the header by
moving 4 zeros (a long int of 0) into the slot reserved for this value. It
could have also done an 'ltell()' as we did above. It closes and is done.
#include "a:std.h"
#include <ry.h>
main()
{
RFILE *op_mbx;
_allocp = NULL;
if ((op_mbx = ropen("a:sysop.mbx", 'd', 2)) != ERROR) {
printf("\n\tI already have one....");
rclose(op_mbx);
exit();
}
if ((op_mbx = rcreat("a:sysop.mbx", 'd', 2)) == ERROR) {
printf("\n\tCan't, problems with sysop's mailbox, sorry.");
exit();
}
rputc(0x1a, op_mbx); /* put ^z at end for cp/m */
setmem(op_mbx + 1, 0, 4); /* put pos. 0 at EOF slot */
rclose(op_mbx);
}
See chapter 8, The Unix System Interface, of The C Programming Language for more info on random file use.
Along with the ry code you will also need the 'lx.crl' file, which
contains the long int functions. Several additions/corrections to the original
cug release of long.c are included in this file. Remember that you need a z-80
to run the long int package. The alternative would be to rewrite 'li()' in
8080 .csm code.
lue.